﻿//modified from AvalonEdit.Sample

using System;
using System.Collections.Generic;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Folding;

namespace LogoScriptIDE
{
    /// <summary>
    /// LogoScript的代码折叠策略
    /// </summary>
    public class FoldingStrategy : AbstractFoldingStrategy
    {
        // 以下来自AvalonEdit的用例
        public override IEnumerable<NewFolding> CreateNewFoldings(
            TextDocument document, out int firstErrorOffset)
        {
            firstErrorOffset = -1;
            return CreateNewFoldings(document);
        }

        /// <summary>
        /// 生成代码折叠
        /// </summary>
        /// <param name="document">代码</param>
        /// <returns>代码折叠设置列表</returns>
        public IEnumerable<NewFolding> CreateNewFoldings(ITextSource document)
        {
            List<NewFolding> newFoldings = new List<NewFolding>();

            // 记录每个大括号的位置
            Stack<int> startOffsets = new Stack<int>();
            // 记录本行起始位置
            int lastNewLineOffset = 0;

            // 表示当前字符是否位于注释中
            bool inComment = false;
            // 记录连续的注释行数
            int continueCommentLines = 0;
            // 记录注释开始所处的位置
            int commentOffset = -1;
            // 记录上一个注释字符
            int lastCommentIndex = -1;
            // 表示当前字符是否位于字符串中
            bool inString = false;
            // 表示当前字符是否是转义字符
            bool charChange = false;

            // 遍历代码文件
            for (int i = 0; i < document.TextLength; i++)
            {
                char c = document.GetCharAt(i);

                // 该字符是注释
                if (c == '`' && !inString && !inComment)
                {
                    inComment = true;
                    continueCommentLines++;
                }

                // 该字符是字符串符号
                if (c == '"' && !inComment)
                {
                    if (!inString)
                    {
                        // 该字符是字符串末尾符号
                        inString = true;
                    }
                    else
                    {
                        if (!charChange)
                            inString = false;
                    }
                }
                
                // 该字符是有效的左大括号
                if (c == '{' && !inComment && !inString)
                {
                    startOffsets.Push(i);
                }
                
                // 该字符是有效的右大括号，并且存在对应的左大括号
                if (c == '}' && startOffsets.Count > 0 && !inComment && !inString)
                {
                    int startOffset = startOffsets.Pop();
                    // 同一行的语句块不进行压缩
                    if (startOffset < lastNewLineOffset)
                    {
                        newFoldings.Add(new NewFolding(startOffset, i + 1));
                    }
                }
                
                // 该字符是换行符
                if (c == '\n' || c == '\r')
                {
                    lastNewLineOffset = i + 1;
                    inComment = false;
                }

                // 该字符是转义字符
                if (c == '\\')
                {
                    if (charChange)
                        charChange = false;
                    else
                        charChange = true;
                }
                else
                {
                    charChange = false;
                }

                if (!Char.IsWhiteSpace(c))
                {
                    if (inComment)
                    {
                        if (commentOffset == -1)
                            commentOffset = i;
                        lastCommentIndex = i;
                    }
                    else
                    {
                        // 如果该字符不是注释字符，并且上一个非空字符还是注释的话
                        // 说明注释结束
                        // 倘若有多行注释，那么这些注释可以被折叠
                        if (commentOffset != -1)
                        {
                            if (continueCommentLines > 1)
                                newFoldings.Add(new NewFolding(commentOffset + 1, lastCommentIndex + 1));
                            commentOffset = -1;
                            lastCommentIndex = -1;
                        }
                        continueCommentLines = 0;
                    }
                }
            }

            // 折叠文件末尾的连续注释行
            if (commentOffset != -1)
            {
                if (continueCommentLines > 1)
                    newFoldings.Add(new NewFolding(commentOffset + 1, lastCommentIndex + 1));
                commentOffset = -1;
                lastCommentIndex = -1;
            }

            // 按代码折叠对的起始位置排序
            newFoldings.Sort((a, b) => a.StartOffset.CompareTo(b.StartOffset));
            return newFoldings;
        }
    }
}
